package com.aiti.lulian.flow.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.aiti.base.core.exception.BusinessException;
import com.aiti.lulian.flow.FlowConstant;
import com.aiti.lulian.flow.business.BusinessFlowService;
import com.aiti.lulian.flow.dto.FlowParamDto;
import com.aiti.lulian.flow.dto.FlowRejectParamDto;
import com.aiti.lulian.flow.entity.CardFlowDefinition;
import com.aiti.lulian.flow.entity.CardFlowInstance;
import com.aiti.lulian.flow.entity.CardFlowNodeDefinition;
import com.aiti.lulian.flow.entity.CardFlowProcess;
import com.aiti.lulian.flow.mapper.CardFlowDefinitionMapper;
import com.aiti.lulian.flow.mapper.CardFlowInstanceMapper;
import com.aiti.lulian.flow.mapper.CardFlowNodeDefinitionMapper;
import com.aiti.lulian.flow.mapper.CardFlowProcessMapper;
import com.aiti.lulian.flow.vo.FlowInstanceProcessVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * description:
 * auth fcb
 * date 2025-09-04 10:16
 **/
@Service
@Slf4j
public class FlowService implements ApplicationContextAware {

    private ApplicationContext applicationContext;
    @Autowired
    private CardFlowDefinitionMapper cardFlowDefinitionMapper;
    @Autowired
    private CardFlowNodeDefinitionMapper cardFlowNodeDefinitionMapper;
    @Autowired
    private CardFlowInstanceMapper cardFlowInstanceMapper;
    @Autowired
    private CardFlowProcessMapper cardFlowProcessMapper;
    @Autowired
    private CardFlowProcessService cardFlowProcessService;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 启动流程
     * @param flowParam
     */
    @Transactional(rollbackFor = Exception.class)
    public String startFlow(FlowParamDto flowParam) {
        // 获取流程定义
        CardFlowDefinition flowDefinition = cardFlowDefinitionMapper.queryByFlowKey(flowParam.getFlowKey());
        if(flowDefinition == null) {
            throw new BusinessException("流程不存在：" + flowParam.getFlowKey());
        }
        // 获取流程节点定义
        List<CardFlowNodeDefinition> nodeDefList = cardFlowNodeDefinitionMapper.queryFlowNodeByFlowId(flowDefinition.getBaseId());
        if(CollectionUtil.isEmpty(nodeDefList)) {
            throw new BusinessException("未查询到该流程节点配置：" + flowParam.getFlowKey());
        }
        // 执行业务回调
        BusinessFlowService businessFlowService = applicationContext.getBean(flowParam.getFlowKey(), BusinessFlowService.class);
        businessFlowService.beforeStartFlow(flowParam);
        String businessId = businessFlowService.saveBusinessData(flowParam);
        // 创建流程实例
        CardFlowInstance flowInstance = this.createFlowInstance(flowDefinition.getBaseId(), businessId, flowParam.getUserId());
        // 创建流程审批记录，第一个开始节点自动通过
        List<CardFlowProcess> processList = new ArrayList<>();
        CardFlowProcess startNodeProcess = this.getStartNodeFlowProcess(flowInstance, nodeDefList);
        processList.add(startNodeProcess);
        CardFlowProcess firstNodeProcess = this.getFirstNodeFlowProcess(flowInstance, nodeDefList, startNodeProcess);
        processList.add(firstNodeProcess);
        // 设置流程当前节点为第一个审批节点
        flowInstance.setCurrentNodeKey(firstNodeProcess.getProcessNodeKey());
        // 保存流程实例和审批记录
        cardFlowInstanceMapper.insert(flowInstance);
        cardFlowProcessService.saveBatch(processList);
        return flowInstance.getBaseId();
    }

    /**
     * 重新发起
     * @param flowParam
     */
    @Transactional(rollbackFor = Exception.class)
    public void restartFlow(FlowParamDto flowParam) {
        // 获取流程实例
        CardFlowInstance flowInstance = cardFlowInstanceMapper.selectById(flowParam.getFlowInstanceId());
        if(flowInstance == null) {
            throw new BusinessException("流程实例不存在");
        }
        if(flowInstance.getProcessStatus() != FlowConstant.REJECTED) {
            throw new BusinessException("流程未驳回，无法重新发起");
        }
        // 获取流程定义
        CardFlowDefinition flowDefinition = cardFlowDefinitionMapper.queryByFlowId(flowInstance.getFlowDefinitionId());
        // 获取流程节点定义
        List<CardFlowNodeDefinition> nodeDefList = cardFlowNodeDefinitionMapper.queryFlowNodeByFlowId(flowDefinition.getBaseId());
        // 执行业务回调
        BusinessFlowService businessFlowService = applicationContext.getBean(flowDefinition.getFlowKey(), BusinessFlowService.class);
        businessFlowService.beforeStartFlow(flowParam);
        businessFlowService.saveBusinessData(flowParam);
        // 查询当前节点的审批记录
        CardFlowProcess currentNodeProcess = cardFlowProcessMapper.queryFlowInstanceLastProcess(flowParam.getFlowInstanceId(), flowInstance.getCurrentNodeKey());
        CardFlowProcess firstNodeProcess = this.getFirstNodeFlowProcess(flowInstance, nodeDefList, currentNodeProcess);
        // 设置流程当前节点为第一个审批节点
        flowInstance.setCurrentNodeKey(firstNodeProcess.getProcessNodeKey());
        cardFlowProcessService.save(firstNodeProcess);
        cardFlowInstanceMapper.updateById(flowInstance);
    }

    /**
     * 审批通过
     * @param flowInstanceId 流程实例id
     * @param userId 用户id
     */
    @Transactional(rollbackFor = Exception.class)
    public void approve(String flowInstanceId, String userId) {
        // 获取流程实例
        CardFlowInstance flowInstance = cardFlowInstanceMapper.selectById(flowInstanceId);
        // 获取流程定义
        CardFlowDefinition flowDefinition = cardFlowDefinitionMapper.queryByFlowId(flowInstance.getFlowDefinitionId());
        // 获取流程节点定义
        List<CardFlowNodeDefinition> nodeDefList = cardFlowNodeDefinitionMapper.queryFlowNodeByFlowId(flowInstance.getFlowDefinitionId());
        // 查询当前节点的审批记录
        CardFlowProcess currentNodeProcess = cardFlowProcessMapper.queryFlowInstanceLastProcess(flowInstanceId, flowInstance.getCurrentNodeKey());
        if(!userId.equals(currentNodeProcess.getAuditUserId())) {
            throw new BusinessException("您不是当前节点的审批人，无法审批流程");
        }
        // 设置当前审批记录状态
        currentNodeProcess.setNodeStatus(FlowConstant.NODE_APPROVED);
        currentNodeProcess.setEndTime(new Date());
        cardFlowProcessMapper.updateById(currentNodeProcess);
        // 执行审批节点通过之后的业务回调
        BusinessFlowService businessFlowService = applicationContext.getBean(flowDefinition.getFlowKey(), BusinessFlowService.class);
        businessFlowService.passNode(flowInstanceId, currentNodeProcess.getProcessNodeKey(), flowInstance.getBusinessId());
        // 查询下一个节点
        CardFlowNodeDefinition nextNode = this.getNextNode(flowInstance, nodeDefList);
        // 如果下一个节点是审批节点
        if(FlowConstant.AUDIT.equals(nextNode.getNodeType())) {
            flowInstance.setCurrentNodeKey(nextNode.getNodeKey());
            flowInstance.setProcessStatus(FlowConstant.PROCESSING);
            // 创建下一个节点的审批记录
            CardFlowProcess nodeFlowProcess = this.getNodeFlowProcess(flowInstance.getBaseId(), nextNode, currentNodeProcess);
            cardFlowProcessService.save(nodeFlowProcess);
            cardFlowInstanceMapper.updateById(flowInstance);
        }
        // 如果下一个节点是结束节点
        else if(FlowConstant.END.equals(nextNode.getNodeType())) {
            flowInstance.setProcessStatus(FlowConstant.COMPLETED);
            flowInstance.setEndTime(new Date());
            flowInstance.setCurrentNodeKey(nextNode.getNodeKey());
            // 创建执行完毕的流程记录
            CardFlowProcess endNodeFlowProcess = this.getEndFlowProcess(flowInstance, nextNode, currentNodeProcess);
            cardFlowProcessService.save(endNodeFlowProcess);
            cardFlowInstanceMapper.updateById(flowInstance);
            // 执行流程完成之后的业务回调
            businessFlowService.flowComplete(flowInstanceId, flowInstance.getBusinessId());
        }
    }

    /**
     * 驳回
     * @param param
     */
    @Transactional(rollbackFor = Exception.class)
    public void reject(FlowRejectParamDto param) {
        // 获取流程实例
        CardFlowInstance flowInstance = cardFlowInstanceMapper.selectById(param.getFlowInstanceId());
        // 获取流程定义
        CardFlowDefinition flowDefinition = cardFlowDefinitionMapper.queryByFlowId(flowInstance.getFlowDefinitionId());
        // 查询当前节点的审批记录
        CardFlowProcess currentNodeProcess = cardFlowProcessMapper.queryFlowInstanceLastProcess(param.getFlowInstanceId(), flowInstance.getCurrentNodeKey());
        // 更新当前审批节点状态
        Date now = new Date();
        currentNodeProcess.setNodeStatus(FlowConstant.NODE_REJECTED);
        currentNodeProcess.setEndTime(now);
        currentNodeProcess.setRemark(param.getRemark());
        cardFlowProcessMapper.updateById(currentNodeProcess);
        // 更新流程实例状态
        flowInstance.setProcessStatus(FlowConstant.REJECTED);
        flowInstance.setEndTime(now);
        cardFlowInstanceMapper.updateById(flowInstance);
        // 执行流程驳回之后的业务回调
        BusinessFlowService businessFlowService = applicationContext.getBean(flowDefinition.getFlowKey(), BusinessFlowService.class);
        businessFlowService.flowReject(param.getFlowInstanceId(), flowInstance.getCurrentNodeKey(), flowInstance.getBusinessId());
    }

    /**
     * 流程的审批记录
     * @param flowInstanceId
     * @return
     */
    public List<FlowInstanceProcessVo> queryFlowInstanceProcess(String flowInstanceId) {
        List<FlowInstanceProcessVo> processList = cardFlowProcessMapper.queryFlowInstanceProcess(flowInstanceId);
        return processList;
    }

    private CardFlowNodeDefinition getNextNode(CardFlowInstance flowInstance, List<CardFlowNodeDefinition> nodeDefList) {
        for(int i=0; i<nodeDefList.size(); i++) {
            if(nodeDefList.get(i).getNodeKey().equals(flowInstance.getCurrentNodeKey())) {
                if((i+1) < nodeDefList.size()) {
                    return nodeDefList.get(i+1);
                }
            }
        }
        return nodeDefList.get(nodeDefList.size() - 1);
    }

    private CardFlowProcess getFirstNodeFlowProcess(CardFlowInstance flowInstance, List<CardFlowNodeDefinition> nodeDefList, CardFlowProcess currentNodeProcess) {
        CardFlowProcess firstNodeProcess = new CardFlowProcess();
        firstNodeProcess.setBaseId(IdUtil.simpleUUID());
        firstNodeProcess.setFlowInstanceId(flowInstance.getBaseId());
        firstNodeProcess.setProcessNodeName(nodeDefList.get(1).getNodeName());
        firstNodeProcess.setProcessNodeKey(nodeDefList.get(1).getNodeKey());
        firstNodeProcess.setAuditUserId(nodeDefList.get(1).getCandidateUserId());
        firstNodeProcess.setStartTime(new Date());
        firstNodeProcess.setNodeStatus(FlowConstant.NODE_PENDING);
        firstNodeProcess.setSort(currentNodeProcess.getSort() + 1);
        return firstNodeProcess;
    }

    private CardFlowProcess getEndFlowProcess(CardFlowInstance flowInstance, CardFlowNodeDefinition endNode, CardFlowProcess currentNodeProcess) {
        CardFlowProcess startNodeProcess = new CardFlowProcess();
        startNodeProcess.setBaseId(IdUtil.simpleUUID());
        startNodeProcess.setFlowInstanceId(flowInstance.getBaseId());
        startNodeProcess.setProcessNodeName(endNode.getNodeName());
        startNodeProcess.setProcessNodeKey(endNode.getNodeKey());
        startNodeProcess.setStartTime(flowInstance.getEndTime());
        startNodeProcess.setEndTime(flowInstance.getEndTime());
        startNodeProcess.setNodeStatus(FlowConstant.NODE_APPROVED);
        startNodeProcess.setSort(currentNodeProcess.getSort() + 1);
        return startNodeProcess;
    }
    private CardFlowProcess getNodeFlowProcess(String instanceId, CardFlowNodeDefinition nodeDef, CardFlowProcess currentNodeProcess) {
        CardFlowProcess startNodeProcess = new CardFlowProcess();
        startNodeProcess.setBaseId(IdUtil.simpleUUID());
        startNodeProcess.setFlowInstanceId(instanceId);
        startNodeProcess.setProcessNodeName(nodeDef.getNodeName());
        startNodeProcess.setProcessNodeKey(nodeDef.getNodeKey());
        startNodeProcess.setAuditUserId(nodeDef.getCandidateUserId());
        startNodeProcess.setStartTime(new Date());
        startNodeProcess.setNodeStatus(FlowConstant.NODE_PENDING);
        startNodeProcess.setSort(currentNodeProcess.getSort() + 1);
        return startNodeProcess;
    }

    private CardFlowProcess getStartNodeFlowProcess(CardFlowInstance flowInstance, List<CardFlowNodeDefinition> nodeDefList) {
        CardFlowProcess startNodeProcess = new CardFlowProcess();
        startNodeProcess.setBaseId(IdUtil.simpleUUID());
        startNodeProcess.setFlowInstanceId(flowInstance.getBaseId());
        startNodeProcess.setProcessNodeName(nodeDefList.get(0).getNodeName());
        startNodeProcess.setProcessNodeKey(nodeDefList.get(0).getNodeKey());
        startNodeProcess.setAuditUserId(flowInstance.getStartUserId());
        startNodeProcess.setStartTime(flowInstance.getStartTime());
        startNodeProcess.setEndTime(flowInstance.getStartTime());
        startNodeProcess.setNodeStatus(FlowConstant.NODE_APPROVED);
        startNodeProcess.setSort(0);
        return startNodeProcess;
    }


    private CardFlowInstance createFlowInstance(String flowDefId, String businessId, String startUserId) {
        CardFlowInstance flowInstance = new CardFlowInstance();
        flowInstance.setBaseId(IdUtil.simpleUUID());
        flowInstance.setFlowDefinitionId(flowDefId);
        flowInstance.setBusinessId(businessId);
        flowInstance.setStartTime(new Date());
        flowInstance.setStartUserId(startUserId);
        flowInstance.setProcessStatus(FlowConstant.INIT);
        return flowInstance;
    }
}
